{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%matplotlib inline \n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Funciones\n",
    "Las funciones son un mecanismo que permite organizar el código de forma estructurada. En esencia, una función es un bloque de código que se asocia a un nombre.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Algunas funciones definidas en Python\n",
    "* Para volcar a un *stream*, usamos `print`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hola, Mundo\n"
     ]
    }
   ],
   "source": [
    "print(\"Hola, Mundo\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* La longitud de una lista, tupla, diccionario, cadena (y de otros objetos) se obtiene con `len`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "La longitud de [1, 3, 5] es 3\n",
      "La longitud de patatas es 7\n",
      "La longitud de {'ana': 13, 'pedro': 18} es 2\n"
     ]
    }
   ],
   "source": [
    "for val in ([1,3,5], \"patatas\", {'ana':13, 'pedro':18}):\n",
    "    print(\"La longitud de %s es %s\" % (val, len(val)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* El mínimo y el máximo de un iterable no vacío se calculan mediante `min` y `max`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-4"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "min([2, 12, -4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'estuve'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max([\"estuve\", \"en\", \"el\", \"campo\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Dado un iterable, con `enumerate` obtenemos los pares (posición, valor)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(0, 'a'), (1, 'u'), (2, 'm'), (3, 'e'), (4, 'n'), (5, 't'), (6, 'a'), (7, 'r')]\n"
     ]
    }
   ],
   "source": [
    "val = \"aumentar\"\n",
    "print(list(enumerate(val)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lo que viene bien si se va a recorrer un iterable y es importante conocer la posición de cada valor:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "En la posicion 0 esta el caracter a\n",
      "En la posicion 1 esta el caracter u\n",
      "En la posicion 2 esta el caracter m\n",
      "En la posicion 3 esta el caracter e\n",
      "En la posicion 4 esta el caracter n\n",
      "En la posicion 5 esta el caracter t\n",
      "En la posicion 6 esta el caracter a\n",
      "En la posicion 7 esta el caracter r\n"
     ]
    }
   ],
   "source": [
    "for ind, v in enumerate(val):\n",
    "    print(\"En la posicion %s esta el caracter %s\" % (ind, v))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* La función *cremallera* (`zip`) recibe uno o más iterables. Devuelve un iterable de tuplas, que van tomando un elemento de cada uno de los inputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('antonio', 24, 'soltero'), ('maria', 31, 'soltero'), ('jose', 12, 'casado'), ('eneas', 82, 'divorciado')]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nombres = [\"antonio\", \"maria\", \"jose\", \"eneas\"]\n",
    "edades = [24, 31, 12, 82]\n",
    "eCivil = ['soltero', 'soltero', 'casado', 'divorciado']\n",
    "print(list(zip(nombres, edades, eCivil)))\n",
    "len(list(zip(nombres, edades, eCivil)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Definición de Funciones\n",
    "Se definen mediante la palabra reservada `def`.\n",
    "Sintaxis:\n",
    "```python\n",
    "def nombreFuncion(arg1, arg2, ...):\n",
    "    bloque\n",
    "```\n",
    "Estas declaraciones funcionan como una instrucción más. Al ejecutarse el efecto es totalmente análogo al de una asignación: introducen en el espacio de nombres un nuevo nombre (el de la función), apuntando a un nuevo valor (el código de la función).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Inicio de ejecucion de la funcion funPrueba\n",
      "Fin de ejecucion de la funcion funPrueba\n"
     ]
    }
   ],
   "source": [
    "def funPrueba():\n",
    "    print('Inicio de ejecucion de la funcion funPrueba')\n",
    "    print('Fin de ejecucion de la funcion funPrueba')\n",
    "funPrueba()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Todas las funciones devuelven un valor, mediante la palabra reservada `return`. Se utiliza en instrucciones de la forma\n",
    "```python\n",
    "return val\n",
    "```\n",
    "El efecto de esta instrucción es que la ejecución de la llamada a la función termina en ese punto, y el valor devuelto es `val`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Si no hay instrucción `return`, la función ejecuta normalmente, y el valor devuelto al final es `None`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los `arg1`, `arg`, etc. de la declaración son *parámetros*. El código en el bloque hace uso de ellos, de forma que con distintos valores para ellos se producen ejecuciones diferentes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NOTA**: A todos los efectos una definición se comporta como una asignación, solo que el valor almacenado es una función."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def valorPresente(flujos, fds):\n",
    "    return sum([f*df for (f, df) in zip(flujos ,fds)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**La capacidad de definir funciones aporta ventajas obvias**. Sobre todo, permite seguir el principio de desarrollo de software [DRY (don't repeat yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), según el cuál cada idea debe estar recogida en un único punto del código.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Invocación de Funciones\n",
    "Las funciones se definen para ser invocadas posteriormente. Basta utilizar la expresión\n",
    "```python\n",
    "nombreFuncion(v1, v2, ...)\n",
    "```\n",
    "para que el código de la función sea ejecutado pasando el valor `v1` a `arg1`, `v2` a `arg2`, etc., hasta que se alcance una instrucción `return`. Ese paso de valores se realiza con el mismo modelo que la asignación, y el de `for`.\n",
    "\n",
    "El valor devuelto es el que se toma como resultado de evaluar la invocación a la función.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Por ejemplo, para usar la función `valorPresente` con unos datos concretos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "El valor presente es 109.41\n"
     ]
    }
   ],
   "source": [
    "vp = valorPresente([5, 5, 105], [0.987, 0.9678, 0.94891])\n",
    "print(\"El valor presente es %0.2f\" % vp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "El valor presente es 109.41\n"
     ]
    }
   ],
   "source": [
    "vp = valorPresente((5, 5, 105), (0.987, 0.9678, 0.94891))\n",
    "print (\"El valor presente es %0.2f\" % vp)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejemplo (revisitado): ecuaciones de segundo grado.\n",
    "Vamos a encapsular el código que ya tenemos para resolver ecuaciones de segundo grado, en una función que llamaremos\n",
    "`solEqSegundoGrado`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El ejemplo de cálculo que ya implementamos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Soluciones: [3.0, -1.0]\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "a, b, c = 1, -2, -3\n",
    "det = b*b - 4*a*c\n",
    "if det > 0:\n",
    "    sols = [(-b + math.sqrt(det)) / (2*a), (-b - math.sqrt(det)) / (2*a)]\n",
    "elif det == 0:\n",
    "    sols = [(-b) / (2*a)]\n",
    "else:\n",
    "    sols = []\n",
    "print(\"Soluciones: %s\" % sols)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import math\n",
    "def ecGrado2(a,b,c):\n",
    "    det= b**2 - 4*a*c\n",
    "    if det > 0:\n",
    "        return [((-b+ math.sqrt(det))/(2*a)),((-b - math.sqrt(det))/(2*a))]\n",
    "    elif det == 0:\n",
    "        return [-b/(2*a)]\n",
    "    else:\n",
    "        return []"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Encapsulado en una función:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def ecGrado2(a, b, c):\n",
    "    det = b*b - 4*a*c\n",
    "    if det > 0:\n",
    "        return [ (-b + math.sqrt(det)) / (2*a), (-b - math.sqrt(det)) / (2*a) ]\n",
    "    elif det == 0:\n",
    "        return [-b/(2*a)]\n",
    "    else:\n",
    "        return []"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La invocamos con el mismo ejemplo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Soluciones: [3.0, -1.0]\n"
     ]
    }
   ],
   "source": [
    "print(\"Soluciones: %s\" % ecGrado2(a=1, b=-2, c=-3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejemplo (revisitado): `sumaNumeros`\n",
    "Hagamos la función que suma los números una lista. Teníamos"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "El total es 10.65.\n"
     ]
    }
   ],
   "source": [
    "nums = [0.23, 3, -5, 12.42]\n",
    "total = 0\n",
    "for e in nums:\n",
    "    total += e\n",
    "print(\"El total es %s.\" % total)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def sumaNumeros(nums):\n",
    "    total=0\n",
    "    for e in nums:\n",
    "        total +=e\n",
    "    return total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12.25"
      ]
     },
     "execution_count": 115,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "numes = [1, 5, 6.25]\n",
    "sumaNumeros(numes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Encapsulamos el código en una función:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def sumaNumeros(nums):\n",
    "    total = 0\n",
    "    for e in nums:\n",
    "        total += e\n",
    "    return total"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La invocamos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10.65"
      ]
     },
     "execution_count": 117,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sumaNumeros([0.23, 3, -5, 12.42])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio (revisitado): Algoritmo de Euclides\n",
    "Implementar la función `mcd(a,b)` que devuelva el MCD de `a` y `b`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recordamos: la implementación de un ejemplo particular que teníamos era:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "El mcm es 2\n"
     ]
    }
   ],
   "source": [
    "a = 10\n",
    "b = 4\n",
    "while b:\n",
    "    (a, b) = (b, a % b)\n",
    "print(\"El mcm es %s\" % a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ver [solución](solutions/funciones/ejer_mcd.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio: Paseo aleatorio\n",
    "En el paseo aleatorio, se parte de un valor inicial $V_0$. Se lanza una moneda equilibrada. Si sale cara, sumamos 1 al valor ($V_{k+1} = V_k + 1$). Si sale cruz, se lo restamos. Este proceso se repite hasta que se alcanza un $V_k$  en el conjunto $B$ (bordes).\n",
    "\n",
    "Lo que se pide es implementar la funcion `caminoAleatorio(v0, bordes)`, que devuelva la lista de valores hasta llegar a un borde."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para generar números pseudo-aleatorios uniformes en $[0,1]$ podemos usar la función `random.choice` del módulo `random`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'C'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "random.choice(['A', 'B', 'C'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ver [solución](solutions/funciones/ejer_paseoAleatorio.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio: ruleta\n",
    "Vamos a jugar a la ruleta, apostando siempre a color. Es ruleta europea: 18 valores rojos, 18 negros y el `0` (con el que todos los que apuestan pierden). Partimos de $v_0$ euros, y nuestro objetivo es apostar continuamente 1 euro a color, hasta que o bien logramos la cantidad $k$ objetivo, o bien nos quedamos sin dinero. Programar la funcion `ruletaObjetivo` que nos devuelve la lista con nuestro *bankroll* en cada momento.\n",
    "\n",
    "Prototipo:\n",
    "```def ruletaObjetivo(v0, k)```\n",
    "\n",
    "Ver [solución](solutions/funciones/ejer_martingala.ipynb)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import random\n",
    "def choiceWeighted(dist):\n",
    "    x = random.uniform(0, 1)\n",
    "    for (suc, prob) in dist.items():\n",
    "        x -= prob\n",
    "        if x <= 0:\n",
    "            return suc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio: bingo\n",
    "Vamos a programar un sencillo juego de bingo. Cada cartón lo codificamos como la lista de enteros que contiene. En `cartones` tenemos todos los cartones que se han vendido."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "cartones = [\n",
    "    [3,10,17,18,28,44,48,54,56,61,63,65,71,72,73],\n",
    "    [4,11,13,20,21,29,30,31,50,55,58,71,78,85,90],\n",
    "    [4,12,14,16,19,33,36,42,48,57,76,78,83,85,91],\n",
    "    [6, 7,11,18,19,24,30,36,39,42,51,65,66,80,89],\n",
    "    [2, 3,10,15,17,26,27,33,76,77,78,83,84,92,95],\n",
    "    [5, 9,13,15,20,29,38,39,42,44,52,76,81,84,98],\n",
    "    [6,10,14,18,20,25,28,33,53,68,74,82,90,91,92],\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El objetivo es implementar la función `simulBingo(cartones)`, que simula el sorteo. Recibe una lista de cartones como la anterior, y devuelve la lista de índices de los cartones ganadores."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Es conveniente conocer `random.shuffle`, que permuta al azar los elementos de una lista, *in-place*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[10, 3, 34, 2, 8, 53, 9]\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "lis = [2, 3, 8, 9, 10, 34, 53]\n",
    "random.shuffle(lis)\n",
    "print(lis)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ver [solución](solutions/funciones/ejer_bingoFun.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio: Búsqueda de ceros de funciones\n",
    "Programar la función `hallaZero`, a la que se le pasa una función `f` (de una variable) y un intervalo (tupla con los dos extremos), y opcionalmente una tolerancia, y devuelve un cero en ese intervalo. En los extremos del intervalo la función debe tomar signos contrarios.\n",
    "\n",
    "Utilizarla en particular para hallar una aproximación de $\\sqrt{5}$.\n",
    "\n",
    "Implementar el método de [la bisección](https://en.wikipedia.org/wiki/Bisection_method), por su sencillez. \n",
    "Al invocarla la función podemos asumir que en los extremos toma valores con signo distinto.\n",
    "\n",
    "Prototipo: \n",
    "\n",
    "```def hallaCero(fun, inter, tol=1E-12)```\n",
    "\n",
    "Si te ayuda, la función `numpy.sign` es la función *signo*. Devuelve +1 para positivos y -1 para negativos.\n",
    "\n",
    "Ver [solución](solutions/funciones/ejer_biseccion.ipynb).\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> En Python todos los elementos son [Ciudadanos de Primer Nivel](https://en.wikipedia.org/wiki/First-class_citizen).\n",
    "Donde otros lenguajes solo admiten valores numéricos, Python admite funciones, clases, módulos, etc.\n",
    "\n",
    "> Las funciones que, o bien reciben como argumentos otras funciones (como ésta), o bien devuelven una función, se denominan de **orden superior**. Python las soporta de forma natural, sin sintaxis especiales."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio: Derivada numérica en un punto\n",
    "Programar la función `derivNum(fun, x0, h)`, a la que se le pasa una función `fun` (de una variable), un punto `x0` y un spread `h`, y devuelve la derivada numérica de la función centrada en ese punto con ese spread $h$.\n",
    "\n",
    "Es decir, el valor de la aproximación\n",
    "$$f'(x_0) \\simeq \\frac{f(x_0+h) - f(x_0-h)}{2h}$$\n",
    "\n",
    "Ver [solución](solutions/funciones/ejer_derivNum.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejemplo: Función derivada numérica\n",
    "Una variante: queremos una función `funDerivNum(fun, h)` que devuelva **otra función**, la derivada numérica.\n",
    "\n",
    "Ya teníamos la derivada en un punto:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def derivNum(fun, x0, h=1E-6):\n",
    "    return (fun(x0 + h) - fun(x0 - h) ) / (2*h)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La función derivada que devolvemos hará esto mismo en el punto que se le pida. Por tanto:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def funDerivNum(fun, h=1E-6):\n",
    "    # Fabricamos la funcion que, en un x0 cualquiera, devuelve la deriv. num.\n",
    "    def der(x0):\n",
    "        return derivNum(fun, x0, h)\n",
    "\n",
    "    # Ahora, simplemente devolvemos esa funcion\n",
    "    return der"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Volvemos al ejemplo seno/coseno:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-0.079120888785145\n",
      "-0.079120888806734\n"
     ]
    }
   ],
   "source": [
    "fDer = funDerivNum(np.sin)  # fDer debería ser casi-casi la función coseno\n",
    "print(\"%0.15f\" % fDer(1.65))\n",
    "print(\"%0.15f\" % np.cos(1.65))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejemplo: Suma de cadenas\n",
    "Programemos la función `sumaCadenas`, que suma todas las cadenas pasadas en una lista."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def sumaCadenas(cads):\n",
    "    total = \"\"\n",
    "    for c in cads:\n",
    "        total += c\n",
    "    return total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'AsesoresFinancierosInternacionales'"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sumaCadenas(['Asesores', 'Financieros', 'Internacionales'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio: Suma de listas\n",
    "Sabemos que para Python, la suma de listas es su concatenación. Programemos sumaListas, que suma las listas pasadas en una lista (de listas).\n",
    "\n",
    "Prototipo: `sumaListas(xss)`.\n",
    "\n",
    "Ver [solución](solutions/funciones/ejer_sumaListas.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejemplo: Suma generalizada\n",
    "Vemos que la implementación de `sumaNumeros`, `sumaCadenas` y `sumaListas` son básicamente iguales, salvo por la inicialización de `total`. Esto viola el principio [DRY (don't repeat yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). En Python es relativamente fácil evitar estas situaciones.\n",
    "\n",
    "Implementemos una versión *polimórfica* de `suma`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def suma(seq):\n",
    "    total = type(seq[0])()  # No obvio: obtiene el elemento neutro de la clase del primer elemento de seq.\n",
    "    for e in seq:\n",
    "        total += e\n",
    "    return total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10.65\n",
      "AsesoresFinancierosInternacionales\n",
      "[2, 'pablo', 3.526, (2, 3, 1), 'alberto']\n"
     ]
    }
   ],
   "source": [
    "print(suma([0.23, 3, -5, 12.42]))\n",
    "print(suma(['Asesores', 'Financieros', 'Internacionales']))\n",
    "print(suma([[2, 'pablo'], [], [3.526, (2,3,1), 'alberto']]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esto ilustra la filosofía del ** *duck typing* **: la función `suma` puede utilizarse con secuencias de datos de cualesquier tipo que defina el interfaz del operador `+`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## *Docstring* de funciones\n",
    "Los *docstring* son cadenas que se colocan como lo primero que aparace en la definición de funciones, módulos o clases. Son interpretadas como documentación, y tratadas de un modo especial (se almacenan en el atributo `__doc__`).\n",
    "\n",
    "Todo el *docstring* debe ir indentado como la función. En la primera línea, la descripción de lo que hace la función. Luego una línea en blanco, y luego los detalles. Por ejemplo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def hallaCero(fun, inter, tol=1E-12):\n",
    "    \"\"\"Calcula el cero de una funcion, dentro de un intervalo.\n",
    "    \n",
    "    Inputs:\n",
    "        * fun: funcion de una variable de la que se calcula el cero\n",
    "        * inter: par (izq, der) con los extremos del intervalo a buscar. \n",
    "          Debe suceder que fun(izq) y fun(der) tengan signos diferentes.\n",
    "        * tol: la tolerancia exigida.\n",
    "        \n",
    "    Output:\n",
    "        El x tal que fun(x)==0 con la tolerancia dada.\n",
    "        \n",
    "    Esta implementacion utiliza el metodo de la biseccion.\n",
    "    \n",
    "    \"\"\"\n",
    "    izq, der = inter\n",
    "    fIzq, fDer = fun(izq), fun(der)\n",
    "    while True:\n",
    "        cen = 0.5 * (izq+der)\n",
    "        fCen = fun(cen)  # Invocacion a la funcion\n",
    "        if np.abs(fCen) < tol:\n",
    "            return cen\n",
    "        if np.sign(fIzq) == np.sign(fCen):\n",
    "            izq, fIzq = cen, fCen\n",
    "        else:\n",
    "            der, fDer = cen, fCen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Al hacer esto, nuestra función dispone de una documentación de ayuda:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "help(hallaCero)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Funciones anónimas: las `lambda`-funciones\n",
    "A veces sucede que necesitamos definir una función que solo se va a usar en un punto, y por tanto puede no merecer la pena darle un nombre. Muchos lenguajes proporcionan soporte para esta situación: las llamadas *funciones anónimas*. En Python, se hace a través de las `lambda`-funciones.\n",
    "\n",
    "La sintaxis es `lambda v1, v2, ..., vs:expr`, para representar la función que recibe las variables `v1`, ..., `vs`, y lo que devuelve es la expresión `expr`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Por ejemplo, la función que dados $n$ y $m$ calcula $(n+2) \\cdot (m+4)$ es:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "lambda n, m: (n+2)*(m+4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Podemos definir una lista de funciones anónimas, y aplicarlas de una en una mediante un bucle:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "funciones = [lambda n, m: (n+2)*(m+4), lambda n, m: (n+3)*(m+5)]\n",
    "for f in funciones:\n",
    "    print(f(10, 13))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En general, se saca partido a este mecanismo cuando se utiliza orden superior."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejemplo: Estimación de $\\sqrt{5}$ como cero de una función\n",
    "Lo que distingue a $\\sqrt{5}$ es que es un $x$ tal que $x^2=5$. O, de otro modo, un cero de la función $f(x) = x^2 - 5$. Usamos la función `hallaCero`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "sq5 = hallaCero(lambda x: x*x-5, [0, 4])\n",
    "print(\"Valor estimado: %0.16f\" % sq5)\n",
    "print(\"Valor real: %0.16f\" % np.sqrt(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### Ejemplo (revisitado): Función derivada numérica.\n",
    "\n",
    "La implementación que hicimos es:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def funDerivNum(fun, h=1E-6):\n",
    "    # Fabricamos la funcion que, en un x0 cualquiera, devuelve la deriv. num.\n",
    "    def der(x0):\n",
    "        return derivNum(fun, x0, h)\n",
    "\n",
    "    # Ahora, simplemente devolvemos esa funcion\n",
    "    return der"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vemos que nos vimos obligados a dar un nombre `der` a la función, antes de devolverla. Es justo el tipo de circunstancia en la que las `lambda`-funciones son útiles:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def funDerivNum(fun, h=1E-6):\n",
    "    return lambda x0: derivNum(fun, x0, h)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicio: Reparto del bote del bingo.\n",
    "Ana, Elena y Paco son aficionados al bingo. Están jugando una partida casera, en la que los cartones que han comprado han sido:\n",
    "```\n",
    "(3,10,17,18,28,44,48,54,56,61,63,65,71,72,73) -> Ana\n",
    "(4,11,13,20,21,29,30,31,50,55,58,71,78,85,90) -> Ana\n",
    "(4,12,14,16,19,33,36,42,48,57,76,78,83,85,91) -> Elena\n",
    "(6, 7,11,18,19,24,30,36,39,42,51,65,66,80,89) -> Paco\n",
    "(2, 3,10,15,17,26,27,33,76,77,78,83,84,92,95) -> Paco\n",
    "(5, 9,13,15,20,29,38,39,42,44,52,76,81,84,98) -> Paco ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pusieron 100 euros por cartón, así que hay 600 euros de bote, que son para el primero que cante bingo (se reparte en partes iguales si varios cantan a la vez).\n",
    "\n",
    "Tras un rato, los números que han salido son:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "vistos = [57,31,85,1,9,51,16,80,11,33,40,4,78,14]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En este momento, por razones de fuerza mayor tienen que suspender la partida. La tarea es estimar el reparto justo del bote, que es el que otorga **a cada uno su ganancia final esperada**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NOTA**: la tarea es suficientemente compleja como para descomponerla en varias funciones."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ver [solución](solutions/funciones/ejer_bingo.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Algunos usos especiales de funciones"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Paso de parámetros con nombre\n",
    "Es posible explicitar el nombre de los argumentos al invocar una función. Por eso"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "hallaCero(lambda x:x*x-5, (0.0, 5.0), 1E-12)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "se puede escribir también como"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "hallaCero(lambda x:x*x-5, (0.0, 5.0), tol=1E-12)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "o incluso"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "hallaCero(inter=(0.0, 5.0), fun=lambda x:x*x-5, tol=1E-12)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Funciones con número variable de argumentos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ya conocemos funciones *built-in* que pueden invocarse pasando un número variable de argumentos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print()\n",
    "print(2,3)\n",
    "print(\"a\", \"b\", \"c\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(list(zip([2,3,4])))\n",
    "print(list(zip([2,3,4], [\"a\", \"b\", \"c\"])))\n",
    "print(list(zip([2,3,4], [\"a\", \"b\", \"c\"], [\"uno\", \"dos\", \"tres\"])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las funciones definidas por el usuario también pueden soportar un número variable de argumentos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def muestraArgumentos(*args, **kwargs):\n",
    "    for val in args:\n",
    "        print(\"Argumento posicional %s\" % val)\n",
    "    for field, val in kwargs.items():\n",
    "        print(\"Argumento con nombre %s y valor %s\" % (field, val))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "muestraArgumentos(2, \"a\", tol=1E-4, amp=1E3, forced=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Recursión\n",
    "Una función se dice recursiva si en su implementación hay invocaciones a sí misma. Eso es permitido, y permite soluciones muy elegantes a algunos problemas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejemplo: Aplanamiento de listas\n",
    "Supón que tenemos una lista, cuyos elementos son números u otras listas, que a su vez contienen números u otras listas. Por ejemplo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "dato = [2, [3, [18, [23, 51], [29], 39], 34, [2,3] ], [12, 19, [40, 50]]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lo que queremos es *aplanar* esa estructura, obteniendo una lista cuyos elementos son los números enterrados en la estructura. Esa tarea se presta a una implementación recursiva directa:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def aplanar(dato):\n",
    "    if isinstance(dato, list):  # Decide si dato es una lista o no\n",
    "        result = []\n",
    "        for elem in dato:\n",
    "            result.extend(aplanar(elem))\n",
    "        return result\n",
    "    else:\n",
    "        return [dato]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 3, 18, 23, 51, 29, 39, 34, 2, 3, 12, 19, 40, 50]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "aplanar(dato)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### Ejercicio: Sublistas de una lista\n",
    "Digamos que una lista $L_1$ es una sublista de $L_2$, si resulta de eliminar de $L_2$ 0 o más elementos. Implementar una función que devuelva la lista de sublistas de una lista dada. El orden de los elementos en la lista devuelta no es importante."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Por ejemplo, `sublistas([4, 2, 3])` debería ser\n",
    "`[ [], [4], [2], [3], [4,2], [4,3], [2,3], [4,2,3] ]`, o una permutación suya."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ver [solución](solutions/funciones/ejer_sublistas.ipynb)."
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "celltoolbar": "Attachments",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
